//-
// ==========================================================================
// Copyright (C) 1995 - 2006 Autodesk, Inc. and/or its licensors.  All
// rights reserved.
//
// The coded instructions, statements, computer programs, and/or related
// material (collectively the "Data") in these files contain unpublished
// information proprietary to Autodesk, Inc. ("Autodesk") and/or its
// licensors, which is protected by U.S. and Canadian federal copyright
// law and by international treaties.
//
// The Data is provided for use exclusively by You. You have the right
// to use, modify, and incorporate this Data into other products for
// purposes authorized by the Autodesk software license agreement,
// without fee.
//
// The copyright notices in the Software and this entire statement,
// including the above license grant, this restriction and the
// following disclaimer, must be included in all copies of the
// Software, in whole or in part, and all derivative works of
// the Software, unless such copies or derivative works are solely
// in the form of machine-executable object code generated by a
// source language processor.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND.
// AUTODESK DOES NOT MAKE AND HEREBY DISCLAIMS ANY EXPRESS OR IMPLIED
// WARRANTIES INCLUDING, BUT NOT LIMITED TO, THE WARRANTIES OF
// NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR
// PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE, OR
// TRADE PRACTICE. IN NO EVENT WILL AUTODESK AND/OR ITS LICENSORS
// BE LIABLE FOR ANY LOST REVENUES, DATA, OR PROFITS, OR SPECIAL,
// DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES, EVEN IF AUTODESK
// AND/OR ITS LICENSORS HAS BEEN ADVISED OF THE POSSIBILITY
// OR PROBABILITY OF SUCH DAMAGES.
//
// ==========================================================================
//+

//	File Name: stereoCameraView.cpp
//
//	Description:
//		A class derived from MPx3dModelView to demonstrate some the render
//		of multiple cameras in a stereoscopic view.  Note, it is important 
//		that you have a graphics card capable of rendering to stereo 
//		buffers. 
//
//	Date: Sept 29, 2006. 
//

#include <stereoCameraView.h>

#include <maya/MItDag.h>
#include <maya/MStringArray.h>
#include <maya/MGlobal.h>

#include <maya/MPoint.h>
#include <maya/MSelectionList.h>
#include <maya/MItSelectionList.h>
#include <maya/MIOStream.h>

stereoCameraView::stereoCameraView() : 
	fLeftBuffer( GL_BACK_LEFT ),
	fRightBuffer( GL_BACK_RIGHT ),
	fValidView( true ),
	fOverrideHUDDraw( false ),
	fOverrideAdornmentDraw( false )
// 
// Description: 
//  Constructor. 
//
{
}

stereoCameraView::~stereoCameraView()
// 
// Description: 
//  Destructor. 
//
{
}

MString stereoCameraView::getCameraHUDName()
//
// Description: 
// 
{
	MString hudName("stereoCameraView: ");

	MDagPath cameraPath;
	getCamera(cameraPath);
	
	cameraPath.pop();
	hudName += cameraPath.partialPathName();
	
	if ( fDisplayMode == kStereo ) { 
		hudName += " (stereo)" ;
	} else if ( fDisplayMode == kLeftCamera ) { 
		hudName += " (left)";
	} else if ( fDisplayMode == kRightCamera ) { 
		hudName += " (right)"; 
	} else { 
		hudName += " (center)"; 
	}
	
	return hudName;
}

void stereoCameraView::preMultipleDraw()
//
//  Description: 
//   Called before all draw passes.  
// 
{
	MDagPath oldCamera;
	MStatus status = getCamera(oldCamera);
	// Set the adornment drawing state ... 
	// 
	setDrawAdornments( !fOverrideAdornmentDraw );

	// Disable HUD drawing ... 
	// 
	setDisplayHUD( !fOverrideHUDDraw ); 

	if (MS::kSuccess != status) {
		status.perror("M3dView::getCamera");
	}
	
	if ( fCenterCamera.isValid() &&  fLeftCamera.isValid() && 
		 fRightCamera.isValid() ) {
		setCamera( fCenterCamera ); 
		fOldCamera = oldCamera;
		fValidView = true; 
	} else { 
		fValidView = false; 
	}
}


void stereoCameraView::postMultipleDraw()
//
// Description: 
//  
{
	if ( fValidView ) { 
		glDrawBuffer( GL_BACK ); 
		if ( fDisplayMode != kStereo ) { 
			if ( fOverrideAdornmentDraw ) { 
				drawAdornmentsNow(); 
			}
			if ( fOverrideHUDDraw ) { 
				drawHUDNow(); 
			}
		} 

		MStatus status = setCamera(fOldCamera);
		
		updateViewingParameters();
		if (MS::kSuccess != status) {
			status.perror("M3dView::setCamera");
		}
	}
}

void stereoCameraView::preMultipleDrawPass(unsigned currentPass)
//
//  Description: 
//   The main work routine. On each draw pass this routine specifies 
//   which buffer we should be drawing in.  
//
{
	MStatus status;
	MDagPath cameraToDraw;

	if ( !fValidView ) { 
		// We don't have valid cameras defined.
		// 
		return ; 
	}

	DisplayMode dm = displayMode(); 
	if ( dm == kStereo ) { 
		if ( currentPass == 0 ) { 
			cameraToDraw = fLeftCamera; 
		} else { 
			cameraToDraw = fRightCamera; 
		}
	} else if ( dm == kLeftCamera ) { 
		cameraToDraw = fLeftCamera; 
	} else if ( dm == kRightCamera ) { 
		cameraToDraw = fRightCamera; 
	} else { 
		cameraToDraw = fCenterCamera; 
	}
	
	if (MS::kSuccess != (status = setCameraInDraw(cameraToDraw))) {
		status.perror("M3dView::setCamera");
		return;
	}

	//	Turn on the display of everything, then selectively disable
	//	things.
	//
	setObjectDisplay(M3dView::kDisplayEverything, true);

	beginGL(); 
	if ( hasStereoBufferSupport() ) { 
		if ( currentPass == 0 ) { 
			glMatrixMode( GL_MODELVIEW ); 
			glDrawBuffer( fLeftBuffer ); 
			glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT |
					 GL_ACCUM_BUFFER_BIT | GL_STENCIL_BUFFER_BIT );
			// The depth buffer bit is assigned to each buffer, so 
			// we must explicitly set it here. 
			// 
			glEnable( GL_DEPTH_TEST ); 
			glDepthFunc( GL_LEQUAL ); 
		} else { 
			glMatrixMode( GL_MODELVIEW ); 
			glDrawBuffer( fRightBuffer ); 
				glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT |
						 GL_ACCUM_BUFFER_BIT | GL_STENCIL_BUFFER_BIT );
			// The depth buffer bit is assigned to each buffer, so 
			// we must explicitly set it here. 
			// 
			glEnable( GL_DEPTH_TEST ); 
			glDepthFunc( GL_LEQUAL ); 
		}
	} else { 
		glMatrixMode( GL_MODELVIEW ); 
		glDrawBuffer( fLeftBuffer ); 
		glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT |
				 GL_ACCUM_BUFFER_BIT | GL_STENCIL_BUFFER_BIT );
		glEnable( GL_DEPTH_TEST );
		glDepthFunc( GL_LEQUAL ); 
	}
	endGL(); 

	setLightingMode(kLightDefault); 	
	updateViewingParameters();
}

void stereoCameraView::postMultipleDrawPass(unsigned index)
{
	//	Until better control over selection and picking is exposed,
	//	turn on the display of everything after drawing. The selection
	//	methods will not consider things that are not visible.
	//
	setObjectDisplay(M3dView::kDisplayEverything, true);
	if ( fDisplayMode == kStereo ) { 
		if ( fOverrideAdornmentDraw ) { 
			drawAdornmentsNow(); 
		}
		if ( fOverrideHUDDraw ) { 
			drawHUDNow(); 
		}
	}
}

bool stereoCameraView::okForMultipleDraw(const MDagPath &dagPath)
//
// Description: 
//  This routine is responsible excluding objects from the draw pass. 
//  In this case, we do not want to draw stereo cameras.  By default 
//  MPx3dModelView class excludes the main camera set in setCamera. 
//  However, it will not exclude the left & right camera because it 
//  has no idea that they are a part of the view. 
//
{
	if ( dagPath == fCenterCamera || 
		 dagPath == fRightCamera ||  
		 dagPath == fLeftCamera ) { 
		return false; 
	}
	return true;
}

unsigned stereoCameraView::multipleDrawPassCount()
//
// Description: 
//  Tells the draw pass engine how many passes we want to draw.  For 
//  stereoscopic. This value should always be two (one for each buffer). 
//  
{
	// Some graphics cards do not support stereo buffers. We need to check the
	// stereo bit to see if we were successfully able to get a stereo buffer. 
	// If we failed to allocate a stereo buffer then we only draw one pass. This 
	// method is called before draw passes. 
	// 
	if ( hasStereoBufferSupport() ) { 
		return 2; 
	}
	return 1; 
}

void * stereoCameraView::creator()
{
	return new stereoCameraView();
}

MString stereoCameraView::viewType() const
//
//	Description:
//    A string object that _uniquely_ represents this view.  You should
//    make sure this value is unique across all views. 
//
{
	return MString("stereoCameraView");
}

bool stereoCameraView::wantStereoGLBuffer() const 
// 
// Description: 
//  This tells the base model view classes to construct a view that 
//  is stereoscopic capable. If the card supports stereo graphics, 
//  then the you will have access to quad buffers left front/back & 
//  right front/back.  If the card does not support stereo graphics, 
//  then a normal double buffer view will be created. 
//
{
	return true;
}

void stereoCameraView::setLeftCamera( MDagPath &camera )
//
// Description: 
//  Specifies the left camera. The dag path must point to
//  a cameraShape (not transform). 
// 
{
	fLeftCamera = camera; 
	updateViewingParameters(); 
	refresh(); 
}

MDagPath stereoCameraView::leftCamera( ) const
// 
// Description: 
//  Returns the left camera. 
//
{
	return fLeftCamera; 
}

void stereoCameraView::setRightCamera( MDagPath &camera )
// 
// Description: 
//  Sets ths right camera. The dag path must point to a 
//  cameraShape (not transform). 
// 
{
	fRightCamera = camera; 
	updateViewingParameters(); 
	refresh(); 
}
	
MDagPath stereoCameraView::rightCamera( ) const
// 
// Description: 
//  Returns the current right camera. 
// 
{
	return fRightCamera; 
}

void stereoCameraView::setCenterCamera( MDagPath &camera )
//
// Description: 
//  Sets the camera that should be in the middle. Dag path must 
//  point to a cameraShape (not transform). 
//
{
	fCenterCamera = camera; 
	updateViewingParameters(); 
	refresh();
}

MDagPath stereoCameraView::centerCamera( ) const
{
	return fCenterCamera;
}
	
void stereoCameraView::setDisplayMode( stereoCameraView::DisplayMode dm )
// 
// Description: 
//   Specifies the display mode. 
//
{
	fDisplayMode = dm; 
	// We need to force a refresh to catch all cases.  The low level view classes
	// are not aware that we are drawing for three different cameras and we must
	// force a refresh when we switch our view. 
	// 
	refresh( false, true ); 
}

stereoCameraView::DisplayMode stereoCameraView::displayMode( ) const
//
// Description: 
//  Return the current display mode as an enum. 
//
{
	return fDisplayMode; 
}

void stereoCameraView::swapLeftRightBuffer()
//
// Description: 
//  Swap the buffer that is used for left camera with the buffer 
//  for the right camera.  Likewise for the right camera.  
//
{
	GLenum tBuffer = fLeftBuffer; 
	fLeftBuffer = fRightBuffer;
	fRightBuffer = tBuffer; 
	refresh();
}

void stereoCameraView::setOverrideHUDDraw( bool v )
{
	fOverrideHUDDraw = v; 
	refresh(); 
}
bool stereoCameraView::overrideHUDDraw() const
{
	return fOverrideHUDDraw; 
}

void stereoCameraView::setOverrideAdornmentDraw( bool v )
{
	fOverrideAdornmentDraw = v ;
	refresh(); 
}

bool stereoCameraView::overrideAdornmentDraw( ) const
{
	return fOverrideAdornmentDraw;
}

const char*  stereoCameraView::className() 
// 
// Description: 
//  Used for debugging only. See errorMacros.h 
//
{
	return "stereoCameraView"; 
}
